﻿using com.nm.Attributes;
using com.nm.Helper;
using com.nm.interfaces;
using com.nm.library.Factorys;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace com.nm.library.com.nm.Helper
{
    /// <summary>
    /// 权限控制
    /// </summary>
    public static class RcHelper
    {
        #region 数据储存支持

        private static RedisClient _rcClient;
        private const string Prefix = "yh:rc:";
        static RedisClient Client => _rcClient ?? (_rcClient = new RedisClient(5));

        private static string GetKey(string key)
        {
            return $"{Prefix}{(ConfigurationHelper.Get("isDebug", false) ? "t" : "d")}:{key}";
        }

        #endregion 数据储存支持

        #region 权限判断

        /// <summary>
        /// 判断管理员是否有权限（拒绝优先）执行指定的控制器（Controller）
        /// 1、拒绝优先，如果控制器基本上已经拒绝了那么直接返回 false（无权限）
        /// 2、角色优先
        /// </summary>
        /// <param name="StaffNo">用户或者角色</param>
        /// <param name="Controller"></param>
        /// <returns></returns>
        public static bool HasRights(string StaffNo, string Controller)
        {
            if (string.IsNullOrWhiteSpace(StaffNo)
                || string.IsNullOrWhiteSpace(Controller))
                return false;
            if (IsDeny(Controller, StaffNo))
                return false;
            return IsAllow(Controller, StaffNo);
        }

        public static IDictionary<string, IList<string>> GetRolesAtAction(string Controller, string Action)
        {
            var key = $"deny:{Controller}:{Action}";
            var l1 = Client.KeyExists(GetKey($"deny:{Controller}"))
                        ? Client.GetStringFromSet(GetKey($"deny:{Controller}"), 0, 100)
                        : new List<string>();
            var l2 = Client.KeyExists(GetKey($"deny:{Controller}:{Action}"))
                        ? Client.GetStringFromSet(GetKey($"deny:{Controller}:{Action}"), 0, 100)
                        : new List<string>();
            var l3 = Client.KeyExists(GetKey($"allow:{Controller}"))
                        ? Client.GetStringFromSet(GetKey($"allow:{Controller}"), 0, 100)
                        : new List<string>();
            var l4 = Client.KeyExists(GetKey($"allow:{Controller}:{Action}"))
                        ? Client.GetStringFromSet(GetKey($"allow:{Controller}:{Action}"), 0, 100)
                        : new List<string>();

            return new Dictionary<string, IList<string>>(){
                { "deny-controller",l1.ToList()},
                { "deny-action",l2.ToList()},
                { "allow-controller", l3.ToList()},
                { "allow-action", l4.ToList()},
            };
        }

        /// <summary>
        /// 判断管理员是否有权限（拒绝优先）执行指定的控制器（Controller）和动作（Action）
        /// 注意：
        /// 1、拒绝优先，如果控制器基本上已经拒绝了那么直接返回 false（无权限）
        /// 2、角色优先
        /// </summary>
        /// <param name="StaffNo">用户或者角色</param>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        /// <returns></returns>
        public static bool HasRights(string StaffNo, string Controller, string Action)
        {
            if (string.IsNullOrWhiteSpace(StaffNo))
                return false;
            if (!AdminUserFactory.Instance.CheckUser(StaffNo))
                return false;
            if (StaffNo.Length == 5)
            {
                //5位，是用户，获取该用户角色
                var roleList = AdminUserFactory.Instance.GetRoleForUser(StaffNo);
                if (roleList.Any(role => IsDeny(Controller, Action, role)))
                {
                    return false;
                }
                if (roleList.Any(role => IsAllow(Controller, Action, role)))
                {
                    return true;
                }
            }
            if (IsDeny(Controller, Action, StaffNo))
                return false;
            return IsAllow(Controller, Action, StaffNo);
        }

        /// <summary>
        /// 是否拒绝
        /// 注意：角色优先
        /// </summary>
        /// <param name="Controller"></param>
        /// <param name="StaffNo"></param>
        /// <returns></returns>
        private static bool IsDeny(string Controller, string StaffNo)
        {
            var key = $"deny:{Controller}";
            if (!Client.KeyExists(GetKey(key)))
                return false;
            return Client.IsStringSet(GetKey(key), StaffNo);
        }

        /// <summary>
        /// 是否拒绝
        /// 注意：角色优先
        /// </summary>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        /// <param name="StaffNo"></param>
        /// <returns></returns>
        private static bool IsDeny(string Controller, string Action, string StaffNo)
        {
            if (IsDeny(Controller, StaffNo))
                return true;
            var key = $"deny:{Controller}:{Action}";
            if (!Client.KeyExists(GetKey(key)))
                return false;
            return Client.IsStringSet(GetKey(key), StaffNo);
        }

        /// <summary>
        /// 是否允许
        /// 注意：角色优先
        /// </summary>
        /// <param name="Controller"></param>
        /// <param name="StaffNo"></param>
        /// <returns></returns>
        private static bool IsAllow(string Controller, string StaffNo)
        {
            var key = $"allow:{Controller}";
            if (!Client.KeyExists(GetKey(key)))
                return false;
            return Client.IsStringSet(GetKey(key), StaffNo);
        }

        /// <summary>
        /// 是否允许，控制器有权限默认
        /// 注意：角色优先
        /// </summary>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        /// <param name="StaffNo"></param>
        /// <returns></returns>
        private static bool IsAllow(string Controller, string Action, string StaffNo)
        {
            var key = $"allow:{Controller}:{Action}";
            if (Client.KeyExists(GetKey(key)))
            {
                if (Client.IsStringSet(GetKey(key), StaffNo))
                    return true;
            }
            return IsAllow(Controller, StaffNo);
        }

        #endregion 权限判断

        #region 权限管理

        ///// <summary>
        ///// 获取用户的所有角色
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <returns></returns>
        //public static IEnumerable<string> GetRoleList(string StaffNo) {
        //	return Client.GetStringFromSet(GetKey($"{StaffNo}:role"), 0, 100);
        //}
        ///// <summary>
        ///// 添加用户到角色
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Role"></param>
        //public static void AddRoleToUser(string StaffNo, string Role) {
        //	Client.AddStringToSet(GetKey($"{StaffNo}:role"), Role);
        //}
        ///// <summary>
        ///// 添加用户到角色
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Roles"></param>
        //public static void AddRoleToUser(string StaffNo, IEnumerable<string> Roles) {
        //	foreach (var role in Roles) {
        //		Client.AddStringToSet(GetKey($"{StaffNo}:role"), role);
        //	}
        //}
        ///// <summary>
        ///// 移除用户的角色
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Role"></param>
        //public static void RemoveRoleFromUser(string StaffNo, string Role) {
        //	Client.RemoveStringFromSet(GetKey($"{StaffNo}:role"), Role);
        //}
        ///// <summary>
        ///// 移除用户的角色
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Roles"></param>
        //public static void RemoveRoleFromUser(string StaffNo, IEnumerable<string> Roles) {
        //	foreach (var role in Roles) {
        //		Client.RemoveStringFromSet(GetKey($"{StaffNo}:role"), role);
        //	}
        //}
        ///// <summary>
        ///// 用户是否拥有某个权限
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Role"></param>
        ///// <returns></returns>
        //public static bool IsUserInRole(string StaffNo, string Role) {
        //	return Client.IsStringSet(GetKey($"{StaffNo}:role"), Role);
        //}
        ///// <summary>
        ///// 判断用户是否拥有角色（多个或一个）
        ///// </summary>
        ///// <param name="StaffNo"></param>
        ///// <param name="Roles"></param>
        ///// <param name="oneOrAll">true的时候 只要拥有一个角色就返回true，否则需要拥有所有的角色才返回ture</param>
        ///// <returns></returns>
        //public static bool IsUserInRole(string StaffNo, IEnumerable<string> Roles, bool oneOrAll) {
        //	if (string.IsNullOrWhiteSpace(StaffNo) || !Roles.Any())
        //		return false;
        //	foreach (var role in Roles) {
        //		if (oneOrAll) {
        //			if (Client.IsStringSet(GetKey($"{StaffNo}:role"), role))
        //				return true;
        //		} else {
        //			if (!Client.IsStringSet(GetKey($"{StaffNo}:role"), role))
        //				return false;
        //		}
        //	}
        //	return true;
        //}

        /// <summary>
        /// 拒绝
        /// </summary>
        /// <param name="Target"></param>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        public static void DenyIt(string Target, string Controller, string Action)
        {
            if (Action == "*")
            {
                Client.AddStringToSet(GetKey($"deny:{Controller}"), Target);
            }
            else
            {
                Client.AddStringToSet(GetKey($"deny:{Controller}:{Action}"), Target);
            }
        }

        /// <summary>
        /// 拒绝
        /// </summary>
        /// <param name="Target"></param>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        public static void RemoveDeny(string Target, string Controller, string Action)
        {
            if (Action == "*")
            {
                Client.RemoveStringFromSet(GetKey($"deny:{Controller}"), Target);
            }
            else
            {
                Client.RemoveStringFromSet(GetKey($"deny:{Controller}:{Action}"), Target);
            }
        }

        /// <summary>
        /// 允许
        /// </summary>
        /// <param name="Target"></param>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        public static void RemoveAllow(string Target, string Controller, string Action)
        {
            if (Action == "*")
            {
                Client.RemoveStringFromSet(GetKey($"allow:{Controller}"), Target);
            }
            else
            {
                Client.RemoveStringFromSet(GetKey($"allow:{Controller}:{Action}"), Target);
            }
        }

        /// <summary>
        /// 允许
        /// </summary>
        /// <param name="Target"></param>
        /// <param name="Controller"></param>
        /// <param name="Action"></param>
        public static void AllowIt(string Target, string Controller, string Action)
        {
            if (Action == "*")
            {
                Client.AddStringToSet(GetKey($"allow:{Controller}"), Target);
            }
            else
            {
                Client.AddStringToSet(GetKey($"allow:{Controller}:{Action}"), Target);
            }
        }

        #endregion 权限管理

        public static void AddController(ISessionContext apiContext)
        {
            if (!apiContext.IsBackend)
                return;
            Client.AddStringToSet(GetKey("controller"), new ControllerData
            {
                Name = apiContext.Controller,
                Description = GetDescription(apiContext.GetType()),
            }.ToJson());
        }

        public static IEnumerable<string> GetAllControllers()
        {
            return Client.GetStringFromSet(GetKey("controller"), 0, 999);
        }

        internal static string GetDescription(Type type)
        {
            var d = type.GetCustomAttribute<DescriptionAttribute>();
            return d?.Description;
            //var description = (DescriptionAttribute)attribs.FirstOrDefault(item => item is DescriptionAttribute);
        }

        public class ControllerData
        {
            [ParamDescription("控制器")]
            public string Name { get; set; }

            [ParamDescription("说明")]
            public string Description { get; set; }
        }
    }
}